home *** CD-ROM | disk | FTP | other *** search
/ Aminet 21 / Aminet 21 (1997)(GTI - Schatztruhe)[!][Oct 1997].iso / Aminet / comm / bbs / cit_src_7H21.lha / roomb.c < prev    next >
C/C++ Source or Header  |  1997-08-21  |  51KB  |  1,948 lines

  1. /**
  2. *       roomb.c
  3. *
  4. * room code for Citadel bulletin board system
  5. *       History
  6. *
  7. * 87Mar30 HAW  Fix for carrier loss in renameRoom, Invite only room
  8. *   bug, add <L>ogin to novice menu for unlogged
  9. * 86Aug16 HAW  Kill history in here due to space problems.
  10. * 85Jan16 JLS  Fix getText so console starting CR creates blank msg.
  11. * 84Jun28 JLS  Enhancement: Creator of a room is listed in Aide>.
  12. * 84Apr04 HAW  Start upgrade to BDS 1.50a.
  13. * 83Feb26 CrT  bug in makeRoom when out of rooms fixed.
  14. * 83Feb26 CrT  matchString made caseless, normalizeString()
  15. * 83Feb26 CrT  "]" directory prompt, user name before prompts
  16. * 82Dec06 CrT  2.00 release.
  17. * 82Nov02 CrT  Cleanup prior to V1.2 mods.
  18. * 82Nov01 CrT  Proofread for CUG distribution.
  19. * 82Mar27 dvm  conversion to v. 1.4 begun
  20. * 82Mar25 dvm  conversion for TRS-80/Omikron test started
  21. * 81Dec21 CrT  Log file...
  22. * 81Dec20 CrT  Messages...
  23. * 81Dec19 CrT  Rooms seem to be working...
  24. * 81Dec12 CrT  Started.
  25. */
  26. #include "ctdl.h"
  27. /**
  28. *       Contents
  29. *
  30. * CleanEnd()    cleans up end of msg
  31. * editText()    handles the end-of-message-entry menu
  32. * findRoom()    find a free room
  33. * getNumber()   prompt user for a number, limited range
  34. * getString()   read a string in from user
  35. * getText()   reads a message in from user
  36. * getYesNo()    prompts for a yes/no response
  37. * givePrompt()    gives usual "THISROOM>" prompt
  38. * indexRooms()    build RAM index to ctdlroom.sys
  39. * initialArchive()  does initial archive of a room
  40. * insertParagraph() inserts paragraph into message
  41. * makeRoom()    make new room via user dialogue
  42. * matchString()   search for given string
  43. * noteRoom()    enter room into RAM index
  44. * renameRoom()    sysop special to rename rooms
  45. * replaceString()   string-substitute for message entry
  46. * searchForRoom()   auxilary to addToList()
  47. *
  48. * # -- operating system dependent function.
  49. */
  50. char     *public_str = " Public";
  51. char     *private_str = " Private";
  52. char     *perm_str = "Permanent";
  53. char     *temp_str = "Temporary";
  54. char      exChar = '?';
  55. char     *on = "on";
  56. char     *off = "off";
  57. char     *no = "no";
  58. char     *yes = "yes";
  59. char      ShType;
  60. char      MsgEntryType = MSG_ENTRY;
  61. extern SListBase Arch_base;
  62. extern aRoom roomBuf;             /** Room buffer */
  63. extern rTable *roomTab;         /** RAM index   */
  64. extern FILE *roomfl;              /** Room file descriptor   */
  65. extern CONFIG cfg;                  /** Other variables   */
  66. extern MessageBuffer msgBuf;        /** Message buffer   */
  67. extern MessageBuffer tempMess;    /** For held messages   */
  68. extern logBuffer logBuf;          /** Person buffer   */
  69. extern logBuffer logTmp;          /** Person buffer   */
  70. extern NetBuffer netBuf;
  71. extern SListBase Moderators;
  72. extern int masterCount,
  73.           thisRoom,
  74.           thisLog;
  75. extern char remoteSysop;
  76. extern char outFlag;              /** Output flag   */
  77. extern char loggedIn;             /** Logged in?   */
  78. extern char haveCarrier;          /** Have carrier?   */
  79. extern char onConsole;            /** How about on Console?   */
  80. extern char whichIO;              /** Where is I/O?   */
  81. extern char *baseRoom;
  82. extern char heldMess;
  83. extern char echo;
  84. extern char echoChar;
  85. extern char *confirm;
  86. extern int thisNet;
  87. extern char *WRITE_TEXT;
  88. extern FILE *netLog;
  89. static char CCAddFlag;
  90.  
  91. /**
  92.   editText()
  93.   This funcion handles the end-of-message-entry menu.
  94.   return TRUE  to save message to disk, FALSE to abort message, and
  95.   ERROR if user decides to continue
  96. **/
  97. int
  98. editText(char *buf, int lim)
  99. {
  100.   extern char *ALL_LOCALS,
  101.            *WRITE_LOCALS;
  102.   char      c,
  103.            *oldRec;
  104.   int       letter;
  105.   char     *OtherEdit[] =
  106.   {
  107.     "Abort\n", "Continue", "Replace String\n", "Print Formatted\n",
  108.     "Global Replace\n",
  109.     " ", " ", " ", " ", " ", " ", " ", " ",
  110.     " ", " ", " ", " ", " ", " ", " ", ""
  111.  
  112.   };
  113.   static struct
  114.     {
  115.       char     *Save;
  116.       char     *Menu;
  117.       char     *Print;
  118.  
  119.     }
  120.   Displays[] =
  121.   {
  122.     {
  123.       "Saving message...\n", "edit.mnu", NULL
  124.  
  125.     }
  126.     ,
  127.     {
  128.       "Saving file description...\n", "descedit.mnu", "File description"
  129.  
  130.     }
  131.     ,
  132.     {
  133.       "Saving information...\n", "infoedit.mnu", "Information"
  134.  
  135.     }
  136.     ,
  137.     {
  138.       "Saving biography...\n", "infoedit.mnu", "Biography"
  139.  
  140.     }
  141.     ,
  142.  
  143.   };
  144.   ExtraOption(OtherEdit, Displays[MsgEntryType].Save);
  145.   if (MsgEntryType == MSG_ENTRY)
  146.     {
  147.       ExtraOption(OtherEdit, "Hold message for later\n");
  148.       ExtraOption(OtherEdit, "Insert paragraph break\n");
  149.       if (thisRoom == MAILROOM && loggedIn)
  150.         ExtraOption(OtherEdit, "Who else\n");
  151.       if (NetValidate(FALSE) && (thisRoom == MAILROOM ||
  152.                                  roomBuf.rbflags.SHARED))
  153.         ExtraOption(OtherEdit, "N");
  154.  
  155.     }
  156.   else if (MsgEntryType == INFO_ENTRY || MsgEntryType == BIO_ENTRY)
  157.     {
  158.       ExtraOption(OtherEdit, "Insert paragraph break\n");
  159.  
  160.     }
  161.   if (onConsole && cfg.BoolFlags.SysopEditor)
  162.     ExtraOption(OtherEdit, "Outside Editor");
  163.   OtherEditOptions(OtherEdit);
  164.   RegisterThisMenu(Displays[MsgEntryType].Menu, OtherEdit);
  165.   do
  166.     {
  167.       outFlag = IMPERVIOUS;
  168.       doCR();
  169.       if (MsgEntryType == MSG_ENTRY && !logBuf.lbflags.NoPrompt)
  170.         Output_Citadel_Message("FLOORN", (long)roomBuf.rbname , NULL, NULL);
  171.       mPrintf("Editor cmd: ");
  172.       switch ((letter = GetMenuChar()))
  173.         {
  174.           case 'A':
  175.             if (strLen(buf) == 0 || getYesNo("CONFRM"))
  176.               {
  177.                 return FALSE;
  178.  
  179.               }
  180.             break;
  181.           case 'C':
  182.             doCR();
  183.             mPrintf("...%s", CleanEnd(buf));
  184.             return ERROR;
  185.           case 'I':
  186.             insertParagraph(buf, lim);
  187.             break;
  188.           case 'W':
  189.             if (thisRoom == MAILROOM)
  190.               {
  191.                 CCAddFlag = TRUE;
  192.                 getList(CCAddDelete, "Other recipients", CC_SIZE, FALSE);
  193.                 CCAddFlag = FALSE;
  194.                 getList(CCAddDelete, "Users to take off Other recipients",CC_SIZE, FALSE);
  195.  
  196.               }
  197.             break;
  198.           case 'P':
  199.             outFlag = OUTOK;
  200.             if (Displays[MsgEntryType].Print != NULL)
  201.               {
  202.                 doCR();
  203.                 mPrintf("   %s", Displays[MsgEntryType].Print);
  204.                 doCR();
  205.                 mFormat(buf);
  206.  
  207.               }
  208.             else
  209.               printMessage(TRUE);
  210.             break;
  211.           case 'R':
  212.           case 'G':
  213.             replaceString(buf, lim, (letter != 'R'));
  214.             break;
  215.           case 'S':
  216.             if (MsgEntryType == MSG_ENTRY && roomBuf.rbflags.SHARED &&
  217.                 cfg.BoolFlags.netParticipant &&
  218.                 loggedIn &&
  219.                 strLen(msgBuf.mbaddr) == 0 &&
  220.                 logBuf.lbflags.NET_PRIVS)
  221.               {
  222.                 if (getYesNo("SVNETM"))
  223.                   if (!netInfo(TRUE))
  224.                     break;
  225.  
  226.               }
  227.             if (MsgEntryType == MSG_ENTRY && roomBuf.rbflags.ANON && loggedIn)
  228.               {
  229.                 if (!getYesNo("SVANON"))
  230.                   {
  231.                     msgBuf.mbdate[0] = 0;
  232.                     strCpy(msgBuf.mbauth, logBuf.lbname);
  233.  
  234.                   }
  235.  
  236.               }
  237.             return TRUE;
  238.           case 'H':
  239.             if (heldMess)
  240.               {
  241.         Output_Citadel_Message("MSGHLD", NULL, NULL, NULL);
  242.         break;
  243.               }
  244.             Output_Citadel_Message("HOLDIT", NULL, NULL, NULL);
  245.             MoveMsgBuffer(&tempMess, &msgBuf);
  246.             heldMess = TRUE;
  247.             return FALSE;
  248.           case '?':
  249.             if ((MsgEntryType == MSG_ENTRY && roomBuf.rbflags.SHARED || thisRoom == MAILROOM)
  250.                 && cfg.BoolFlags.netParticipant &&
  251.                 loggedIn &&
  252.                 logBuf.lbflags.NET_PRIVS)
  253.               mPrintf(" <N>et toggle\n ");
  254.             if (cfg.BoolFlags.SysopEditor && onConsole)
  255.               mPrintf(" <O>utside Editor\n ");
  256.             if (MsgEntryType == MSG_ENTRY && thisRoom == MAILROOM && loggedIn)
  257.               mPrintf(" <W>ho else (Carbon Copies)\n ");
  258.             ShowOutsideEditors();
  259.             break;
  260.           case 'O':
  261.             OutsideEditor();
  262.             break;
  263.           case 'N':
  264.             if (msgBuf.mbaddr[0])
  265.               {
  266.                 c = msgBuf.mbaddr[0];   /* need to remember this */
  267.                 msgBuf.mbaddr[0] = 0;   /* zero for getRecipient */
  268.                 mPrintf("\bormal message\n ");
  269.                 oldRec = strdup(msgBuf.mbto);   /* getRec might zero mbto */
  270.                 if (getRecipient())
  271.                   {
  272.                     msgBuf.mboname[0] = 0;
  273.                     msgBuf.mbdomain[0] = 0;
  274.  
  275.                   }
  276.                 else
  277.                   {
  278.                     msgBuf.mbaddr[0] = c;       /* failed, restore address */
  279.                     strCpy(msgBuf.mbto, oldRec);        /** restore recip        */
  280.  
  281.                   }
  282.                 free(oldRec);
  283.  
  284.               }
  285.             else
  286.               {
  287.                 mPrintf("\betwork message\n ");
  288.                 netInfo(TRUE);
  289.  
  290.               }
  291.             break;
  292.           default:
  293.             RunRemoteEditor(letter);
  294.  
  295.         }
  296.  
  297.     }
  298.   while (onLine());
  299.   if (MsgEntryType == MSG_ENTRY && loggedIn)
  300.     SaveInterrupted(&msgBuf);
  301.   return FALSE;
  302.  
  303. }
  304. /*
  305.  * * CCAddDelete() * * This function handles adding or deleting Other
  306.  * Recipients.
  307.  */
  308. int
  309. CCAddDelete(char *name)
  310. {
  311.   label     person;
  312.   char      buf[CC_SIZE],
  313.             isdomain;
  314.   char      system[(2 * NAMESIZE) + 10],
  315.            *domain;
  316.   int       result,
  317.             cost;
  318.  
  319.   result = SepNameSystem(name, person, system, &netBuf);
  320.   strCpy(buf, name);
  321.   if (result == IS_SYSTEM)
  322.     sPrintf(buf, "%s @%s", person, system);
  323.   isdomain = (domain = strchr(system, '_')) != NULL;
  324.   if (isdomain)
  325.     domain += 2;        /*
  326.                          * formats to "system _ domain", so we cheat
  327.                          */
  328.   if (!CCAddFlag)
  329.     {
  330.  /*
  331.   * Take it off the list
  332.   */
  333.       KillData(&msgBuf.mbCC, buf);
  334.  
  335.     }
  336.   else
  337.     {
  338.       switch (result)
  339.         {
  340.           case BAD_FORMAT:
  341.       Output_Citadel_Message("NOTFND", (long)name , NULL, NULL);
  342.             break;
  343.           case SYSTEM_IS_US:
  344.             break;      /*
  345.                          * error message handled in SepNameSystem
  346.                          */
  347.           case NO_SYSTEM:
  348.       Output_Citadel_Message("NOTFND", (long)name , NULL, NULL);
  349.             break;
  350.           case IS_SYSTEM:
  351.             cost = (isdomain) ? FindCost(domain) : !netBuf.nbflags.local;
  352.             if (logBuf.credit < cost)
  353.               {
  354.                 if (HalfSysop())
  355.                   {
  356.                     logBuf.credit += cost;
  357.  
  358.                   }
  359.                 else
  360.                   {
  361.         Output_Citadel_Message("NOCRDT", NULL, NULL, NULL);
  362.                     break;
  363.  
  364.                   }
  365.  
  366.               }
  367.             else if (!logBuf.lbflags.NET_PRIVS)
  368.               {
  369.         Output_Citadel_Message("NONETP", NULL, NULL, NULL);
  370.         break;
  371.  
  372.               }
  373.             AddData(&msgBuf.mbCC, strdup(buf), NULL, TRUE);
  374.             break;
  375.           case NOT_SYSTEM:
  376.             if (PersonExists(name) == ERROR)
  377.         Output_Citadel_Message("NOTFND", (long)name , NULL, NULL);
  378.             else
  379.               {
  380.                 if (strCmpU(name, logBuf.lbname) == SAMESTRING)
  381.        Output_Citadel_Message("NOSELF", NULL, NULL, NULL);
  382.                 else if (!msgBuf.mbaddr[0] &&
  383.                          strCmpU(name, msgBuf.mbto) == SAMESTRING)
  384.        Output_Citadel_Message("NODUPT", NULL, NULL, NULL);
  385.                 else
  386.                   AddData(&msgBuf.mbCC, strdup(name), NULL, TRUE);
  387.  
  388.               }
  389.             break;
  390.  
  391.         }
  392.  
  393.     }
  394.   return TRUE;
  395.  
  396. }
  397. /*
  398.  * * SepNameSystem() * * This will parse an Other Recipient spec.
  399.  */
  400. char
  401. SepNameSystem(char *string, char *person, char *system, NetBuffer * buf)
  402. {
  403.   char     *c;
  404.   label     domain;
  405.   char      dup,
  406.             work[150];            /*
  407.  
  408.                                    *
  409.                                    * * should be sufficient
  410.                                    */
  411.   int       slot;
  412.  
  413.   strCpy(work, string);
  414.   if (cfg.BoolFlags.debug)
  415.     splitF(NULL, " SepNameSystem:%s\n", string);
  416.   if ((c = strchr(work, '@')) == NULL)
  417.     {
  418.       if (strLen(work) >= NAMESIZE)
  419.         return BAD_FORMAT;
  420.       strCpy(person, string);
  421.       return (char)NOT_SYSTEM;
  422.  
  423.     }
  424.   *c++ = 0;
  425.   NormStr(work);
  426.   NormStr(c);
  427.   if (cfg.BoolFlags.debug)
  428.     splitF(NULL, "        person:%s\n", work);
  429.   if (cfg.BoolFlags.debug)
  430.     splitF(NULL, "        System:%s\n", c);
  431.   if (strLen(c) >= NAMESIZE * 2 || strLen(work) >= NAMESIZE)
  432.     return (char)BAD_FORMAT;
  433.   strCpy(system, c);
  434.   strCpy(person, work);
  435.   if (buf == NULL)
  436.     return (char)IS_SYSTEM;     /* very minor cheat - see CTDL.C */
  437.   if (cfg.BoolFlags.debug)
  438.     splitF(NULL, "    Search for:%s\n", system);
  439.   if ((slot = searchNameNet(c, buf)) != ERROR)
  440.     {
  441.  /*
  442.   * try secondary lists
  443.   */
  444.       strCpy(system, buf->netName);     /*
  445.                                          * get "real" name
  446.                                          */
  447.       if (cfg.BoolFlags.debug)
  448.         splitF(NULL, "       found:%s\n", system);
  449.       if (buf->nbflags.local || buf->nbflags.RouteLock)
  450.         {
  451.           return (char)IS_SYSTEM;
  452.  
  453.         }
  454.  
  455.     }
  456.   if (cfg.BoolFlags.debug)
  457.     splitF(NULL, "  Still Search:%s\n", system);
  458.   if (SystemInSecondary(c, domain, &dup))
  459.     {
  460.       if (dup)
  461.         {
  462.      /*
  463.       * oops
  464.       */
  465.           if (slot == ERROR)
  466.        Output_Citadel_Message("DUPDOM", (long)c, NULL, NULL);
  467.           return (char) ((slot == ERROR) ? NO_SYSTEM : IS_SYSTEM);
  468.  
  469.         }
  470.       if (strCmpU(domain, cfg.nodeDomain + cfg.codeBuf) == SAMESTRING &&
  471.           (strCmpU(c, cfg.nodeName + cfg.codeBuf) == SAMESTRING ||
  472.            strCmpU(c, UseNetAlias(cfg.nodeName + cfg.codeBuf, TRUE))
  473.            == SAMESTRING))
  474.         {
  475.           mPrintf("Hey, that's this system!\n ");
  476.           return (char) SYSTEM_IS_US;
  477.  
  478.         }
  479.       sPrintf(system, "%s _ %s", c, domain);
  480.       return (char)IS_SYSTEM;
  481.  
  482.     }
  483.   if (cfg.BoolFlags.debug)
  484.     splitF(NULL, "(%d)= SepNameSystem(%s,%s,%s,%x)\n ", slot, string, person, system, buf);
  485.   return (char)((slot == ERROR) ? NO_SYSTEM : IS_SYSTEM);
  486.  
  487. }
  488. /*
  489.  * * findRoom() * * This function finds and returns the # of a free room
  490.  * slot if possible, * else ERROR.
  491.  */
  492. int
  493. findRoom()
  494. {
  495.   int       roomRover;
  496.  
  497.   for (roomRover = 0; roomRover < MAXROOMS; roomRover++)
  498.     {
  499.       if (roomTab[roomRover].rtflags.INUSE == 0)
  500.         return roomRover;
  501.  
  502.     }
  503.   return ERROR;
  504.  
  505. }
  506. /*
  507.  * * getNumber() * * This prompts for a number in (bottom, top) range.
  508.  */
  509. long
  510. getNumber(char *prompt, long bottom, long top)
  511. {
  512.   long      try;
  513.   char      numstring[NAMESIZE];
  514.  
  515.   do
  516.     {
  517.       if (!gotCarrier())
  518.         {
  519.           haveCarrier = FALSE;
  520.         };
  521.       Output_Citadel_Message(prompt,NULL, NULL, NULL);
  522.       getString("", numstring, NAMESIZE, 0);
  523.       try = atol(numstring);
  524.       if( try < bottom || try > top)
  525.         Output_Citadel_Message("RANGEV",bottom, top, NULL);
  526.     }
  527.   while ((try < bottom || try > top) && onLine());
  528.   return (long) try;
  529.  
  530. }
  531. /*
  532.  * * getString() * * This gets a string from the user.
  533.  */
  534. int
  535. getString(char *prompt, char *buf, int lim, int Flags)
  536. {
  537.   int       toReturn;
  538.   extern int crtColumn;
  539.   char      oldEcho;
  540.  
  541.   outFlag = IMPERVIOUS;
  542.   if( prompt != NULL )
  543.     {
  544.     if (strLen(prompt) > 0)
  545.       {
  546.       doCR();
  547.       Output_Citadel_Message(prompt,NULL,NULL,NULL);
  548.       };
  549.     };
  550.   oldEcho = echo;
  551.   if (Flags & NO_ECHO)
  552.     {
  553.       echo = NEITHER;
  554.       echoChar = 'X';
  555.  
  556.     }
  557.   outFlag = OUTOK;
  558.   Flags |= CR_ON_ABORT;
  559.   if ((toReturn = BlindString(buf, lim, Flags, iChar, oChar, 0)) == BACKED_OUT)
  560.     return toReturn;
  561.   echo = oldEcho;
  562.   crtColumn = 1;
  563.   return toReturn;
  564.  
  565. }
  566. /*
  567.  * * BlindString() * * Gets a string blind to source and sink.
  568.  */
  569. int
  570. BlindString(char *buf, int lim, int Flags,
  571.             char      (*input) (void), void (*output) (char c), char Echo)
  572. {
  573.   char      c;
  574.   int       i;
  575.  
  576.   i = 0;
  577.   while (onLine() && (c = (*input) (), c != NEWLINE && c != '\r' && i < lim))
  578.     {
  579.       if (!gotCarrier())
  580.         {
  581.           haveCarrier = FALSE;
  582.         };
  583.       if (Echo)
  584.         (*output) (c);
  585.  /*
  586.   * handle delete chars:
  587.   */
  588.       if (c == BACKSPACE)
  589.         {
  590.           (*output) (' ');
  591.           (*output) (BACKSPACE);
  592.           if (i > 0)
  593.             i--;
  594.           else if (Flags & BS_VALID)
  595.             {
  596.               return BACKED_OUT;
  597.  
  598.             }
  599.           else
  600.             {
  601.               (*output) (' ');
  602.               (*output) (BELL);
  603.  
  604.             }
  605.  
  606.         }
  607.       else if (c)
  608.         buf[i++] = c;
  609.       if (i >= lim)
  610.         {
  611.           (*output) (BELL);
  612.           (*output) (BACKSPACE);
  613.           i--;
  614.  
  615.         }
  616.  /*
  617.   * kludge to return immediately on single '?':
  618.   */
  619.       if ((Flags & QUEST_SPECIAL) && *buf == exChar)
  620.         {
  621.           if ((Flags & CR_ON_ABORT))
  622.             doCR();
  623.      /*
  624.       * i--;
  625.       */
  626.           break;
  627.  
  628.         }
  629.  
  630.     }
  631.   buf[i] = '\0';
  632.   return GOOD_SELECT;
  633.  
  634. }
  635. /*
  636.  * * getText() * * This manages reading a message from the user. * Returns
  637.  * TRUE if user decides to save it, else FALSE (whether held or * aborted).
  638.  */
  639. char
  640. getText(int uploading)
  641. {
  642.   extern PROTO_TABLE Table[];
  643.   extern char *R_SH_MARK,
  644.             EndWithCR;
  645.   int       i,
  646.             toReturn;
  647.  
  648. /*
  649.  * msgBuf.mbtext[-1] = NEWLINE;
  650.  */
  651.   if (uploading == ASCII)
  652.     {
  653.     Output_Citadel_Message("UPLDAS", NULL, NULL, NULL);
  654.       outFlag = OUTOK;
  655.       if (msgBuf.mbtext[0])
  656.         CleanEnd(msgBuf.mbtext);
  657.       EndWithCR = FALSE;
  658.       printMessage(TRUE);
  659.       EndWithCR = TRUE;
  660.       outFlag = OUTOK;
  661.  
  662.     }
  663.   else
  664.     {
  665.       if (!expert)
  666.         tutorial(Table[uploading].UpBlbName, TRUE);
  667.       if (!getYesNo("RDYBEG"))
  668.         return FALSE;
  669.       masterCount = 0;
  670.       if (uploading <= TOP_PROTOCOL)
  671.         {
  672.           if (Reception(uploading, putBufChar) != TRAN_SUCCESS)
  673.             return FALSE;
  674.  
  675.         }
  676.       else
  677.         {
  678.           if (EatExtMessage(uploading) != TRAN_SUCCESS)
  679.             return FALSE;
  680.  
  681.         }
  682.  
  683.     }
  684.   toReturn = GetBalance(uploading, msgBuf.mbtext, MAXTEXT);
  685.   if (toReturn == TRUE)
  686.     {
  687.  /*
  688.   * Filter null messages
  689.   */
  690.       toReturn = FALSE;
  691.       for (i = 0; msgBuf.mbtext[i] != 0 && !toReturn; i++)
  692.         toReturn = (msgBuf.mbtext[i] > ' ' && msgBuf.mbtext[i] < 127);
  693.  
  694.     }
  695.   return (char) toReturn;
  696.  
  697. }
  698. /*
  699.  * * GetBalance() * * This function gets the balance of text for a message.
  700.  */
  701. char
  702. GetBalance(int uploading, char *buf, int size)
  703. {
  704.   char      c,
  705.             beeped = FALSE;
  706.   int       i,
  707.             toReturn;
  708.  
  709.   do
  710.     {
  711.       i = strLen(buf);
  712.       if (uploading == ASCII)
  713.         while (
  714.                 !(
  715.                    (c = iChar()) == NEWLINE &&
  716.                    (i == 0 || buf[i - 1] == NEWLINE)
  717.                 )
  718.                 && i < size - 1
  719.                 && onLine()
  720.           )
  721.           {
  722.             if (c != BACKSPACE)
  723.               {
  724.                 if (c == ESC)
  725.                   {
  726.                     if (iChar() == '[')
  727.                       {
  728.                         iChar();
  729.                         continue;       /*
  730.                                          * arrow key.
  731.                                          */
  732.  
  733.                       }
  734.  
  735.                   }
  736.                 if (c != 0 && c != XOFF && c != XON)
  737.                   buf[i++] = c;
  738.                 if (i > size - 80 && !beeped)
  739.                   {
  740.                     beeped = TRUE;
  741.                     oChar(BELL);
  742.  
  743.                   }
  744.  
  745.               }
  746.             else
  747.               {
  748.            /*
  749.             * handle delete chars:
  750.             */
  751.                 oChar(' ');
  752.                 oChar(BACKSPACE);
  753.                 if (i > 0 && buf[i - 1] != NEWLINE)
  754.                   i--;
  755.                 else
  756.                   oChar(BELL);
  757.  
  758.               }
  759.             buf[i] = 0;         /*
  760.                                  * null to terminate message
  761.                                  */
  762.             if (i == size - 1)
  763.               {
  764.                 mPrintf(" buffer overflow\n ");
  765.                 while (receive(2) != ERROR)
  766.                   ;
  767.  
  768.               }
  769.  
  770.           }
  771.       if (i == size - 1)
  772.         buf[--i] = 0;   /*
  773.                          * fixes an odd buffer overflow problem
  774.                          */
  775.  /*
  776.   * couldn't edit the message otherwise
  777.   */
  778.       toReturn = editText(buf, size - 1);
  779.       uploading = ASCII;
  780.  
  781.     }
  782.   while ((toReturn == ERROR) && onLine());
  783.   return (char) toReturn;
  784.  
  785. }
  786. /*
  787.  * * getYesNo() * * This prompts for a yes/no response and gets it.
  788.  */
  789. char
  790. getYesNo(char *prompt)
  791. {
  792.   int       toReturn;
  793.   extern char ConOnly;
  794.   char      (*input) (void);
  795.  
  796.   input = ConOnly ? ( int (*)(void) )getCh : ( int (*)(void) )iChar;
  797.   for (doCR(), toReturn = ERROR; toReturn == ERROR && onLine();)
  798.     {
  799.       outFlag = IMPERVIOUS;
  800.       Output_Citadel_Message(prompt,NULL,NULL,NULL);
  801.       switch (toUpper((*input) ()))
  802.         {
  803.           case 'Y': toReturn = TRUE;            break;
  804.           case 'N': toReturn = FALSE;           break;
  805.           case '\0':toReturn = TRUE;            break;
  806.         };
  807.       doCR();
  808.  
  809.     }
  810.   outFlag = OUTOK;
  811.   return (char) toReturn;
  812.  
  813. }
  814. /*
  815.  * * givePrompt() * * This function simply prints the usual "CURRENTROOM>"
  816.  * prompt -- not as simple * as it may seem.
  817.  */
  818. void
  819. givePrompt()
  820. {
  821.   outFlag = IMPERVIOUS;
  822.   doCR();
  823.   ScreenUser();
  824.   if (!loggedIn)
  825.     Output_Citadel_Message("NOTLGN", NULL, NULL, NULL);
  826.   Output_Citadel_Message("PROMPT"
  827.          ,(long)( (thisRoom == MAILROOM || loggedIn ||cfg.BoolFlags.unlogReadOk) ?
  828.              "<N>ew messages" : " ")
  829.          ,(long)( (thisRoom == MAILROOM || loggedIn ||cfg.BoolFlags.unlogEnterOk)?
  830.              "<E>nter" : " " )
  831.          , NULL);
  832.  
  833.   if (!expert) doCR();
  834.   if (roomBuf.rbflags.READ_ONLY)
  835.     {
  836.     Output_Citadel_Message("READOL", NULL, NULL, NULL);
  837.     doCR();
  838.  
  839.     }
  840.   mPrintf("%s ", formRoom(thisRoom, FALSE, TRUE));
  841.   if (strCmp(roomBuf.rbname, roomTab[thisRoom].rtname) != SAMESTRING)
  842.     {
  843.       printf("thisRoom=%d, rbname=-%s-, rtname=-%s-\n", thisRoom,
  844.              roomBuf.rbname, roomTab[thisRoom].rtname);
  845.       crashout("Dependent variables mismatch!");
  846.  
  847.     }
  848.   outFlag = OUTOK;
  849.  
  850. }
  851. /*
  852.  * * indexRooms() * * This function will try to find and free an empty room.
  853.  */
  854. void
  855. indexRooms()
  856. {
  857.   int       goodRoom,
  858.             slot;
  859.  
  860.   for (slot = 0; slot < MAXROOMS; slot++)
  861.     {
  862.       if (roomTab[slot].rtflags.INUSE == 1)
  863.         {
  864.           goodRoom = FALSE;
  865.           if (roomTab[slot].rtlastMessage > cfg.oldest ||
  866.               roomTab[slot].rtflags.PERMROOM == 1)
  867.             {
  868.               goodRoom = TRUE;
  869.  
  870.             }
  871.           if (!goodRoom)
  872.             {
  873.               getRoom(slot);
  874.               KillInfo(roomTab[slot].rtname);
  875.               roomBuf.rbflags.INUSE = 0;
  876.               putRoom(slot);
  877.               strCat(msgBuf.mbtext, roomBuf.rbname);
  878.               strCat(msgBuf.mbtext, "> ");
  879.               noteRoom();
  880.  
  881.             }
  882.  
  883.         }
  884.  
  885.     }
  886.  
  887. }
  888.  
  889. /*
  890.  * * insertParagraph() * * This inserts a paragraph (CR/Space) into a
  891.  * message. * (By Jay Johnson of The Phoenix)
  892.  */
  893. void
  894. insertParagraph(char *buf, int lim)
  895. {
  896.   char      oldString[2 * SECTSIZE];
  897.   char     *loc,
  898.            *textEnd;
  899.   char     *pc;
  900.   int       length;
  901.  
  902.   for (textEnd = buf, length = 0; *textEnd; length++, textEnd++) ;
  903.   if (lim - length < 3)
  904.     {
  905.     Output_Citadel_Message("NORMMG", NULL, NULL, NULL);
  906.     return;
  907.  
  908.     }
  909.   getString("GETPAR", oldString, (2 * SECTSIZE), 0);
  910.   if ((loc = matchString(buf, oldString, textEnd)) == NULL)
  911.     {
  912.     Output_Citadel_Message("NOTFND", NULL, NULL, NULL);
  913.     return;
  914.  
  915.     }
  916.   for (pc = textEnd; pc >= loc; pc--)
  917.     {
  918.       *(pc + 2) = *pc;
  919.  
  920.     }
  921.   *loc++ = '\n';
  922.   *loc = ' ';
  923.  
  924. }
  925. /*
  926.  * * makeRoom() * * This function constructs a new room via dialogue with
  927.  * user.
  928.  */
  929. void
  930. makeRoom()
  931. {
  932.   label     nm,
  933.             oldName;
  934.   int       CurrentFloor,
  935.             oldRoom;
  936.  
  937.   CurrentFloor = thisFloor;
  938.   oldRoom = thisRoom;
  939. /*
  940.  * update lastMessage for current room:
  941.  */
  942.   logBuf.lbgen[thisRoom] = roomBuf.rbgen << GENSHIFT;
  943.   strCpy(oldName, roomBuf.rbname);
  944.   if ((thisRoom = findRoom()) == ERROR)
  945.     {
  946.       indexRooms();     /*
  947.                          * try and reclaim an empty room
  948.                          */
  949.       if ((thisRoom = findRoom()) == ERROR)
  950.         {
  951.     Output_Citadel_Message("NORMMG", NULL, NULL, NULL);
  952.      /*
  953.       * may have reclaimed old room, so:
  954.       */
  955.           if (roomExists(oldName) == ERROR)
  956.             strCpy(oldName, baseRoom);
  957.           getRoom(roomExists(oldName));
  958.           return;
  959.  
  960.         }
  961.  
  962.     }
  963.   getNormStr("ROOMNM", nm, NAMESIZE, 0);
  964.   if (strLen(nm) == 0)
  965.     {
  966.       if (roomExists(oldName) == ERROR)
  967.         strCpy(oldName, baseRoom);
  968.       getRoom(roomExists(oldName));
  969.       return;
  970.  
  971.     }
  972.   if (roomExists(nm) >= 0)
  973.     {
  974.     Output_Citadel_Message("ALRDYE", (long)nm, NULL, NULL);
  975.  /*
  976.   * may have reclaimed old room, so:
  977.   */
  978.       if (roomExists(oldName) == ERROR)
  979.         strCpy(oldName, baseRoom);
  980.       getRoom(roomExists(oldName));
  981.       return;
  982.  
  983.     }
  984.   if (!expert)
  985.     tutorial("newroom.blb", TRUE);
  986.   zero_struct(roomBuf.rbflags);
  987.   roomBuf.rbflags.INUSE = TRUE;
  988.   if (getYesNo("MKRMPD"))
  989.     roomBuf.rbflags.PUBLIC = TRUE;
  990.   else
  991.     roomBuf.rbflags.PUBLIC = FALSE;
  992.   mPrintf("'%s', a %s room", nm,
  993.           roomBuf.rbflags.PUBLIC == 1 ? "public" : "private"
  994.     );
  995.   if (!getYesNo("MKRMIS"))
  996.     {
  997.  /*
  998.   * may have reclaimed old room, so:
  999.   */
  1000.       if (roomExists(oldName) == ERROR)
  1001.         strCpy(oldName, baseRoom);
  1002.       getRoom(roomExists(oldName));
  1003.       return;
  1004.  
  1005.     }
  1006.   else if (roomExists(oldName) == ERROR)
  1007.     oldRoom = -1;       /*
  1008.                          * in case
  1009.                          */
  1010.   strCpy(roomBuf.rbname, nm);
  1011.   memset(roomBuf.msg, 0, MSG_BULK);     /*
  1012.                                          * mark all slots empty
  1013.                                          */
  1014.   roomBuf.rbgen = (roomTab[thisRoom].rtgen + 1) % MAXGEN;
  1015.   roomBuf.rbFlIndex = CurrentFloor;
  1016.   KillData(&Moderators, NtoStrInit(thisRoom, "", 0, TRUE));
  1017.   WriteAList(&Moderators, "ctdlmodr.sys", WrtNtoStr);
  1018.   KillData(&Arch_base, NtoStrInit(thisRoom, "", 0, TRUE));
  1019.   WriteAList(&Arch_base, "ctdlarch.sys", WrtArchRec);
  1020.   noteRoom();   /*
  1021.                  * index new room
  1022.                  */
  1023.   RoomSys(thisRoom);    /*
  1024.                          * initialize directory area
  1025.                          */
  1026.   putRoom(thisRoom);
  1027.   ZeroMsgBuffer(&msgBuf);
  1028.   Output_Citadel_Message("ENTINF",(long)roomBuf.rbname, NULL, NULL);
  1029.   EditInfo();   /*
  1030.                  * creator gets to set information
  1031.                  */
  1032.   msgBuf.mboname[0] = 0;        /*
  1033.                                  * icky kludge
  1034.                                  */
  1035.   if (strLen(msgBuf.mbtext) != 0 &&
  1036.       getYesNo("MKINFO"))
  1037.     procMessage(ASCII, FALSE);
  1038. /*
  1039.  * update logBuf:
  1040.  */
  1041.   logBuf.lbgen[thisRoom] = roomBuf.rbgen << GENSHIFT;
  1042.   ZeroMsgBuffer(&msgBuf);
  1043.   sPrintf(msgBuf.mbtext, "%s created by %s.", formRoom(thisRoom, FALSE, FALSE),
  1044.           logBuf.lbname);
  1045.   aideMessage(NULL, FALSE);
  1046.   roomTab[thisRoom].rtlastNet = 0l;
  1047.   if (oldRoom != -1)
  1048.     UngotoMaintain(oldRoom);
  1049.  
  1050. }
  1051. /*
  1052.  * * WriteAList() * * This writes a Num->String list to disk.
  1053.  */
  1054. void
  1055. WriteAList(SListBase * base, char *fn, void (*func) ())
  1056. {
  1057.   SYS_FILE  name;
  1058.   extern FILE *upfd;
  1059.  
  1060.   makeSysName(name, fn, &cfg.roomArea);
  1061.   if ((upfd = safeopen(name, WRITE_TEXT)) != NULL)
  1062.     {
  1063.       RunList(base, func);
  1064.       fclose(upfd);
  1065.  
  1066.     }
  1067.   else
  1068.     Output_Citadel_Message("NOOPEN", (long)name, NULL, NULL);
  1069. }
  1070. /*
  1071.  * * matchString() * * This searches for match to given string.  Runs
  1072.  * backward through buffer so * we get most recent error first.  Returns loc
  1073.  * of match, else NULL.
  1074.  */
  1075. char     *
  1076. matchString(char *buf, char *pattern, char *bufEnd)
  1077. {
  1078.   char     *loc,
  1079.            *pc1,
  1080.            *pc2;
  1081.   char      foundIt;
  1082.  
  1083.   for (loc = bufEnd, foundIt = FALSE; !foundIt && --loc >= buf;)
  1084.     {
  1085.       for (pc1 = pattern, pc2 = loc, foundIt = TRUE; *pc1 && foundIt;)
  1086.         {
  1087.           if (!(toUpper(*pc1++) == toUpper(*pc2++)))
  1088.             foundIt = FALSE;
  1089.  
  1090.         }
  1091.  
  1092.     }
  1093.   return foundIt ? loc : NULL;
  1094.  
  1095. }
  1096. /*
  1097.  * * getNormStr() * * This function gets a string and deletes leading &
  1098.  * trailing blanks etc.
  1099.  */
  1100. int
  1101. getNormStr(char *prompt, char *s, int size, int Flags)
  1102. {
  1103.   int       toReturn;
  1104.  
  1105.   if ((toReturn = getString(prompt, s, size, Flags)) != BACKED_OUT)
  1106.     if (*s != exChar && *s != '\0')
  1107.       NormStr(s);
  1108.   return toReturn;
  1109.  
  1110. }
  1111. /*
  1112.  * * noteRoom() * * This will enter a room into RAM index array.
  1113.  */
  1114. void
  1115. noteRoom()
  1116. {
  1117.   int       i;
  1118.   MSG_NUMBER last;
  1119.  
  1120.   last = 0l;
  1121. #ifdef NORMAL_MESSAGES
  1122.   for (i = 0; i < ((thisRoom == MAILROOM) ? MAILSLOTS : MSGSPERRM); i++)
  1123.     {
  1124.       if (roomBuf.msg[i].rbmsgNo > last &&
  1125.           roomBuf.msg[i].rbmsgNo <= cfg.newest)
  1126.         {
  1127.           last = roomBuf.msg[i].rbmsgNo;
  1128.  
  1129.         }
  1130.  
  1131.     }
  1132. #else
  1133.   for (i = 0; i < ((thisRoom == MAILROOM) ? MAILSLOTS : MSGSPERRM); i++)
  1134.     {
  1135.       if ((roomBuf.msg[i].rbmsgNo & (~S_MSG_MASK)) &&
  1136.           (roomBuf.msg[i].rbmsgNo & S_MSG_MASK) > cfg.oldest)
  1137.         last = S_MSG_MASK;
  1138.       if ((roomBuf.msg[i].rbmsgNo & S_MSG_MASK) > last &&
  1139.           (roomBuf.msg[i].rbmsgNo & S_MSG_MASK) <= cfg.newest)
  1140.         {
  1141.           last = (roomBuf.msg[i].rbmsgNo & S_MSG_MASK);
  1142.  
  1143.         }
  1144.  
  1145.     }
  1146. #endif
  1147.   roomTab[thisRoom].rtlastMessage = last;
  1148.   strCpy(roomTab[thisRoom].rtname, roomBuf.rbname);
  1149.   roomTab[thisRoom].rtgen = roomBuf.rbgen;
  1150.   memcpy(&roomTab[thisRoom].rtflags, &roomBuf.rbflags,
  1151.         (long)sizeof roomBuf.rbflags);
  1152.   roomTab[thisRoom].rtShareType = roomBuf.rbShareType;
  1153.   roomTab[thisRoom].rtFlIndex = roomBuf.rbFlIndex;
  1154.  
  1155. }
  1156. char      DoWritePrivs;
  1157.  
  1158. /*
  1159.  * * renameRoom() * * This is a sysop special fn -- it handles all room
  1160.  * editing. * Returns:  TRUE on success else FALSE.
  1161.  */
  1162. char
  1163. renameRoom()
  1164. {
  1165.   int       c,
  1166.             r;
  1167.   char     *buffer,
  1168.             wasRO,
  1169.             wasShared,
  1170.             wasAnon,
  1171.            *save,
  1172.             workbuf[200];
  1173.   extern char *APrivateRoom;
  1174.   char      doAideMessage = 0;    /*
  1175.  
  1176.                                    *
  1177.                                    * * Counter to determine if aide msg
  1178.                                    * needed
  1179.                                    */
  1180.   extern char *WRITE_TEXT;
  1181.   extern FILE *upfd;
  1182.   char     *RoomEditOpts[] =
  1183.   {
  1184.     "X\bExit room editing\n", "Name change\n", "Temporary room\n",
  1185.     "Private setting\n", "Lure users to room\n", "Only Invitational\n",
  1186.     "Innominate status\n", "Values\n", "Withdraw Invitations\n",
  1187.     "Edit information\n", "Read only\n",
  1188.     " ", " ", " ", " ", " ", " ", " ", " ", ""
  1189.  
  1190.   };
  1191.  
  1192.   if (thisRoom == LOBBY || thisRoom == MAILROOM || thisRoom == AIDEROOM)
  1193.     {
  1194.     Output_Citadel_Message("WARNSP", NULL, NULL, NULL);
  1195.     if (!getYesNo("ALLOWS"))
  1196.       return FALSE;
  1197.  
  1198.     };
  1199.   wasShared = roomBuf.rbflags.SHARED;
  1200.   wasAnon = roomBuf.rbflags.ANON;
  1201.   wasRO = roomBuf.rbflags.READ_ONLY;
  1202.   ZeroMsgBuffer(&msgBuf);
  1203.   sPrintf(msgBuf.mbtext, "%s, formerly ", formRoom(thisRoom, FALSE, FALSE));
  1204.   formatSummary(lbyte(msgBuf.mbtext), FALSE);
  1205.   buffer = strdup(msgBuf.mbtext);
  1206.   if (HalfSysop())
  1207.     {
  1208.       ExtraOption(RoomEditOpts, "Archive status\n");
  1209.       ExtraOption(RoomEditOpts, "Directory status\n");
  1210.       if (cfg.BoolFlags.netParticipant)
  1211.         {
  1212.           ExtraOption(RoomEditOpts, "Backbone setting\n");
  1213.           ExtraOption(RoomEditOpts, "Shared room\n");
  1214.  
  1215.         }
  1216.       if (roomBuf.rbflags.ISDIR)
  1217.         {
  1218.           ExtraOption(RoomEditOpts, "U");
  1219.  
  1220.         }
  1221.       if (cfg.BoolFlags.netParticipant && roomBuf.rbflags.ISDIR)
  1222.         {
  1223.           ExtraOption(RoomEditOpts, "Z\bNetwork Downloadable\n");
  1224.  
  1225.         }
  1226.  
  1227.     }
  1228.   if (aide)
  1229.     ExtraOption(RoomEditOpts, "Moderator setting\n");
  1230.   RegisterThisMenu(HalfSysop()? "rooms.mnu" : "rooma.mnu", RoomEditOpts);
  1231.   c = 0;        /*
  1232.                  * Init
  1233.                  */
  1234.   while (c != 'X' && onLine())
  1235.     {
  1236.       mPrintf("\n Room Editing Command: ");
  1237.       c = GetMenuChar();
  1238.       switch (c)
  1239.         {
  1240.           case 'X':
  1241.             break;
  1242.           case 'A':
  1243.             doAideMessage++;
  1244.             roomBuf.rbflags.ARCHIVE = getYesNo("ACTARC");
  1245.             if (roomBuf.rbflags.ARCHIVE)
  1246.               {
  1247.                 getString("EFILEN", workbuf, (sizeof workbuf) - 1, 0);
  1248.                 if (strLen(workbuf) == 0)
  1249.                   {
  1250.                     roomBuf.rbflags.ARCHIVE = FALSE;
  1251.                     break;
  1252.  
  1253.                   }
  1254.                 c = (int) getNumber("ROLLOV", -1, 32000);
  1255.                 AddData(&Arch_base, NtoStrInit(thisRoom, workbuf, c, FALSE),
  1256.                         NULL, TRUE      /*
  1257.                                          * kill duplicates
  1258.                                          */ );
  1259.                 WriteAList(&Arch_base, "ctdlarch.sys", WrtArchRec);
  1260.                 if (getYesNo("INITAR"))
  1261.                   initialArchive(workbuf);
  1262.  
  1263.               }
  1264.             break;
  1265.           case 'N':
  1266.             getNormStr("ROOMNM", workbuf, NAMESIZE, 0);
  1267.             r = roomExists(workbuf);
  1268.             if (r >= 0 && r != thisRoom)
  1269.               {
  1270.                 mPrintf("A %s exists already!\n", workbuf);
  1271.                 break;
  1272.  
  1273.               }
  1274.             else
  1275.               {
  1276.                 ChangeInfoName(workbuf);
  1277.                 strCpy(roomBuf.rbname, workbuf);        /*
  1278.                                                          * also in room
  1279.                                                          * itself
  1280.                                                          */
  1281.                 doAideMessage++;
  1282.  
  1283.               }
  1284.             if (!getYesNo("EDIFRM"))
  1285.               break;
  1286.           case 'E':
  1287.             save = strdup(msgBuf.mbtext);
  1288.             EditInfo();
  1289.             RegisterThisMenu(SomeSysop()? "rooms.mnu" : "rooma.mnu", RoomEditOpts);
  1290.             strcpy(msgBuf.mbtext, save);
  1291.             free(save);
  1292.             break;
  1293.           case 'B':
  1294.             if (!roomBuf.rbflags.SHARED)
  1295.               {
  1296.         Output_Citadel_Message("NOTSHR", NULL, NULL, NULL);
  1297.                     break;
  1298.  
  1299.               }
  1300.             roomBuf.rbShareType = (getYesNo("SYSBKN")) ?
  1301.               BACKBONE : PEON;
  1302.             if (roomBuf.rbShareType == BACKBONE)
  1303.               {
  1304.                 doCR();
  1305.                 ShType = ACTIVE_BACKBONE;
  1306.                 getList(knownHosts,
  1307.                         "Systems that you will be an Active Backbone for",NAMESIZE, FALSE);
  1308.                 ShType = PASS_BACKBONE;
  1309.                 getList(knownHosts,
  1310.                         "Systems that you will be a Passive Backbone for",NAMESIZE, FALSE);
  1311.                 ShType = PEON;
  1312.                 getList(knownHosts,
  1313.                         "Systems that should be returned to Peon status",NAMESIZE, FALSE);
  1314.  
  1315.               }
  1316.             break;
  1317.           case 'M':
  1318.             if (WhoIsModerator(workbuf))
  1319.               {
  1320.                 if (strLen(workbuf))
  1321.                   AddData(&Moderators, NtoStrInit(thisRoom, workbuf, 0,
  1322.                                                   FALSE), NULL, TRUE);
  1323.                 else
  1324.                   KillData(&Moderators, NtoStrInit(thisRoom, "", 0, TRUE));
  1325.  
  1326.               }
  1327.             WriteAList(&Moderators, "ctdlmodr.sys", WrtNtoStr);
  1328.             doAideMessage++;
  1329.             break;
  1330.           case 'D':
  1331.             doAideMessage++;
  1332.             roomBuf.rbflags.ISDIR = getYesNo("ACTDIR");
  1333.             if (roomBuf.rbflags.ISDIR)
  1334.               {
  1335.                 if ((roomBuf.rbflags.ISDIR = getArea(&roomBuf)))
  1336.                   roomBuf.rbflags.PERMROOM = TRUE;
  1337.                 else
  1338.                   break;
  1339.  
  1340.               }
  1341.             else
  1342.               break;
  1343.           case 'U':
  1344.             doAideMessage++;
  1345.             if (c == 'U') mPrintf("load/Download room\n ");
  1346.             roomBuf.rbflags.UPLOAD   = getYesNo("UPLDAL");
  1347.             roomBuf.rbflags.DOWNLOAD = getYesNo("DNLDAL");
  1348.             if (!roomBuf.rbflags.UPLOAD && !roomBuf.rbflags.DOWNLOAD)
  1349.         Output_Citadel_Message("STRNGE",NULL, NULL, NULL);
  1350.             break;
  1351.           case 'T':
  1352.             if (roomBuf.rbflags.ISDIR)
  1353.         Output_Citadel_Message("DIRRMP", NULL, NULL, NULL);
  1354.             else
  1355.               {
  1356.                 roomBuf.rbflags.PERMROOM = !getYesNo("RMTEMP");
  1357.                 doAideMessage++;
  1358.  
  1359.               }
  1360.             break;
  1361.           case 'P':
  1362.             doAideMessage++;
  1363.             roomBuf.rbflags.PUBLIC = !getYesNo("MKRMPR");
  1364.             if (!roomBuf.rbflags.PUBLIC)
  1365.               {
  1366.                 if (getYesNo("CSNAUS"))
  1367.                   {
  1368.                     if (!wasShared || getYesNo("WRNSHR"))
  1369.                       {
  1370.                         roomBuf.rbgen = (roomBuf.rbgen + 1) % MAXGEN;
  1371.                         logBuf.lbgen[thisRoom] = (logBuf.lbgen[thisRoom]
  1372.                                                   & CALLMASK) +
  1373.                           (roomBuf.rbgen << GENSHIFT);
  1374.                         roomTab[thisRoom].rtgen = roomBuf.rbgen;
  1375.  
  1376.                       }
  1377.  
  1378.                   }
  1379.  
  1380.               }
  1381.             break;
  1382.           case 'S':
  1383.             roomBuf.rbflags.SHARED = getYesNo("NTSHRN");
  1384.             if (roomBuf.rbflags.SHARED)
  1385.               {
  1386.                 if (!wasShared)
  1387.                   {
  1388.                     roomBuf.rbflags.PERMROOM = TRUE;
  1389.                     doAideMessage++;
  1390.                /*
  1391.                 * cosmetic bug fix
  1392.                 */
  1393.                     roomTab[thisRoom].rtflags.SHARED = TRUE;
  1394.  
  1395.                   }
  1396.                 getList(addToList, wasShared ?
  1397.                         "Systems to add to the network list for this room" :
  1398.                   "Systems to network this room with", NAMESIZE, FALSE);
  1399.                 if (wasShared)
  1400.                   getList(killFromList,
  1401.                   "Systems to take off the network list", NAMESIZE, FALSE);
  1402.                 roomBuf.rbflags.AUTO_NET =
  1403.                   getYesNo("MSGSNT");
  1404.                 if (roomBuf.rbflags.AUTO_NET)
  1405.                   roomBuf.rbflags.ALL_NET =
  1406.                     getYesNo("EVNNET");
  1407.  
  1408.               }
  1409.             else
  1410.               {
  1411.                 if (wasShared)
  1412.                   doAideMessage++;
  1413.                 if (!roomBuf.rbflags.ISDIR)
  1414.                   roomBuf.rbflags.PERMROOM = FALSE;
  1415.  
  1416.               }
  1417.             break;
  1418.           case 'L':
  1419.             getList(makeKnown, "Users to be invited", NAMESIZE, FALSE);
  1420.             break;
  1421.           case 'R':
  1422.             if ((roomBuf.rbflags.READ_ONLY = getYesNo("MKREAD")))
  1423.               {
  1424.                 DoWritePrivs = TRUE;
  1425.                 getList(WritePrivs, "Users needing write privileges",   NAMESIZE, FALSE);
  1426.                 DoWritePrivs = FALSE;
  1427.                 getList(WritePrivs, "Users losing write privileges",    NAMESIZE, FALSE);
  1428.  
  1429.               }
  1430.             if (roomBuf.rbflags.READ_ONLY != wasRO)
  1431.               doAideMessage++;
  1432.             break;
  1433.           case 'O':
  1434.             doAideMessage++;
  1435.             if ((roomBuf.rbflags.INVITE = getYesNo("MKINVT")))
  1436.               {
  1437.                 roomBuf.rbflags.PUBLIC = FALSE;
  1438.                 if (getYesNo("CSNAUS"))
  1439.                   {
  1440.                     if (!wasShared || getYesNo("WRNSHR"))
  1441.                       {
  1442.                         roomBuf.rbgen = (roomBuf.rbgen + 1) % MAXGEN;
  1443.                         logBuf.lbgen[thisRoom] = (logBuf.lbgen[thisRoom] &
  1444.                                                   CALLMASK) +
  1445.                           (roomBuf.rbgen << GENSHIFT);
  1446.                         roomTab[thisRoom].rtgen = roomBuf.rbgen;
  1447.  
  1448.                       }
  1449.  
  1450.                   }
  1451.                 getList(makeKnown, "Users to be invited", NAMESIZE, FALSE);
  1452.  
  1453.               }
  1454.             break;
  1455.           case 'I':
  1456.             roomBuf.rbflags.ANON = getYesNo("MKANON");
  1457.             if (roomBuf.rbflags.ANON != wasAnon)
  1458.               doAideMessage++;
  1459.             break;
  1460.           case 'V':
  1461.             mPrintf("%s is %s.", roomBuf.rbname,
  1462.                     formatSummary(msgBuf.mbtext, TRUE));
  1463.             break;
  1464.           case 'W':
  1465.             getList(makeUnknown, "Users to be kicked out", NAMESIZE, FALSE);
  1466.             break;
  1467.           case 'Z':
  1468.             doAideMessage++;
  1469.             roomBuf.rbflags.NO_NET_DOWNLOAD =
  1470.               !getYesNo("ALOWNT");
  1471.             break;
  1472.  
  1473.         }
  1474.  
  1475.     }
  1476.   noteRoom();
  1477.   putRoom(thisRoom);
  1478.   if (doAideMessage)
  1479.     {
  1480.       ZeroMsgBuffer(&msgBuf);
  1481.       sPrintf(msgBuf.mbtext, "%s, has been edited to %s, ", buffer,
  1482.               formRoom(thisRoom, FALSE, FALSE));
  1483.       formatSummary(lbyte(msgBuf.mbtext), FALSE);
  1484.       sPrintf(lbyte(msgBuf.mbtext), ", by %s.", logBuf.lbname);
  1485.       aideMessage(NULL, FALSE);
  1486.  
  1487.     }
  1488.   free(buffer);
  1489.   return TRUE;
  1490.  
  1491. }
  1492. /*
  1493.  * * WhoIsModerator() * * This handles adding moderating-type people.
  1494.  */
  1495. char
  1496. WhoIsModerator(char *buf)
  1497. {
  1498.   if (!getXString("MODRTR", buf, NAMESIZE, "no moderator", ""))
  1499.     return FALSE;
  1500.   if (strLen(buf) != 0)
  1501.     if (findPerson(buf, &logTmp) == ERROR)
  1502.       {
  1503.       Output_Citadel_Message("NOTFND", (long)buf, NULL, NULL);
  1504.       return FALSE;
  1505.  
  1506.       }
  1507.   return TRUE;
  1508.  
  1509. }
  1510. /*
  1511.  * * formatSummary() * * This is responsible for formatting a summary of the
  1512.  * current room.
  1513.  */
  1514. char     *
  1515. formatSummary(char *buffer, char NotFinal)
  1516. {
  1517.   char     *c;
  1518.   int       size;
  1519.  
  1520.   sPrintf(buffer, "a%s, ",
  1521.           roomBuf.rbflags.INVITE ? "n Invitation only" :
  1522.           roomBuf.rbflags.PUBLIC ? public_str : private_str);
  1523.   sPrintf(lbyte(buffer), "%s",
  1524.           roomBuf.rbflags.PERMROOM ? perm_str : temp_str);
  1525.   if (roomBuf.rbflags.ARCHIVE)
  1526.     {
  1527.       sPrintf(lbyte(buffer), ", Archived ('%s', ",
  1528.               AskForNSMap(&Arch_base, thisRoom));
  1529.       if ((size = GetArchSize(thisRoom)) == 0)
  1530.         strCat(buffer, "no rollover)");
  1531.       else
  1532.         sPrintf(lbyte(buffer), "rollover at %dK)", size);
  1533.  
  1534.     }
  1535.   if (roomBuf.rbflags.ANON)
  1536.     strCat(buffer, ", Anonymous");
  1537.   if (roomBuf.rbflags.READ_ONLY)
  1538.     strCat(buffer, ", Read-Only");
  1539.   if (roomBuf.rbflags.SHARED)
  1540.     {
  1541.       strCat(buffer, ", Shared");
  1542.       if (roomBuf.rbflags.AUTO_NET)
  1543.         {
  1544.           strCat(buffer, " (autonet for ");
  1545.           strCat(buffer, roomBuf.rbflags.ALL_NET ? "all users)" :
  1546.                  "net-priv users)");
  1547.  
  1548.         }
  1549.  
  1550.     }
  1551.   if (roomBuf.rbflags.ISDIR)
  1552.     {
  1553.       strCat(buffer, ", Directory (");
  1554.       if (NotFinal || !(roomBuf.rbflags.INVITE || !roomBuf.rbflags.PUBLIC))
  1555.         {
  1556.           dirString(lbyte(buffer), &roomBuf.rbArea);
  1557.           strCat(buffer, ", ");
  1558.  
  1559.         }
  1560.       if (roomBuf.rbflags.UPLOAD)
  1561.         strCat(buffer, "uploads, ");
  1562.       if (roomBuf.rbflags.DOWNLOAD)
  1563.         strCat(buffer, "downloads, ");
  1564.       sPrintf(lbyte(buffer), "%snet downloadable",
  1565.               roomBuf.rbflags.NO_NET_DOWNLOAD ? "not " : "");
  1566.       strCat(buffer, ")");
  1567.  
  1568.     }
  1569.   strCat(buffer, " room");
  1570.   if (strLen(c = AskForNSMap(&Moderators, thisRoom)) != 0)
  1571.     sPrintf(lbyte(buffer), " (Moderator is %s)", c);
  1572.   if (roomBuf.rbflags.SHARED && NotFinal)
  1573.     ParticipatingNodes(buffer);
  1574.   return buffer;
  1575.  
  1576. }
  1577. MenuId    GetListId;
  1578.  
  1579. /**
  1580.   getList()
  1581.   This will get a list of names and blindly process them.
  1582. **/
  1583. void
  1584. getList(int (*fn) (char *data), char *prompt, int size, char Sysop)
  1585. {
  1586.   char     *buffer;
  1587.   if (cfg.BoolFlags.debug)
  1588.     splitF(NULL, " getList(%08.8lx,%s,%d,%s)\n",fn,prompt,size, (Sysop) ? "TRUE" : "FALSE");
  1589.   buffer = GetDynamic(size + 1);
  1590.   GetListId = NO_MENU;
  1591.   mPrintf("\n %s (Empty line to end)...\n", prompt);
  1592.   do
  1593.     {
  1594.       if (Sysop)  SysopPrintf(GetListId, " : ");
  1595.       else        mPrintf(" : ");
  1596.       SysopContinualString(GetListId, "", buffer, size, 0);
  1597.       if (strLen(buffer) != 0) if (!(*fn) (buffer)) break;
  1598.  
  1599.     }
  1600.   while (strLen(buffer) != 0);
  1601.   if (Sysop)
  1602.     SysopCloseContinual(GetListId);
  1603.   free(buffer);
  1604. }
  1605. /*
  1606.  * * replaceString() * * This function corrects typos in message entry.
  1607.  */
  1608. #define REPLACE_SIZE    2000
  1609. void
  1610. replaceString(char *buf, int lim, char Global)
  1611. {
  1612.   char      oldString[2 * SECTSIZE];
  1613.   char     *newString;            /*
  1614.  
  1615.                                    *
  1616.                                    * * Eliminate stack overflows in Turbo c
  1617.                                    */
  1618.   char     *loc,
  1619.            *oldloc,
  1620.            *textEnd;
  1621.   char     *pc;
  1622.   int       incr,
  1623.             length,
  1624.             oldLen,
  1625.             newLen,
  1626.             maxLen;
  1627.  
  1628. /*
  1629.  * find terminal null
  1630.  */
  1631.   textEnd = lbyte(buf);
  1632.   length = strLen(buf);
  1633.   getString("GETSTR", oldString, (2 * SECTSIZE), 0);
  1634.   if ((oldLen = strLen(oldString)) == 0)
  1635.     return;
  1636.   if ((oldloc = loc = matchString(buf, oldString, textEnd)) == NULL)
  1637.     {
  1638.       mPrintf("Not found...\n ");
  1639.       return;
  1640.  
  1641.     }
  1642. /*
  1643.  * so we never have too long of a replacement string (unless Global R.)
  1644.  */
  1645.   maxLen = minimum(REPLACE_SIZE, (MAXTEXT - (length - oldLen)));
  1646.   newString = GetDynamic(REPLACE_SIZE);
  1647.   getString("GETREP", newString, maxLen, 0);
  1648.   newLen = strLen(newString);
  1649.   do
  1650.     {
  1651.       textEnd = lbyte(buf);
  1652.       length = textEnd - buf;
  1653.       if ((newLen - oldLen) >= lim - length)
  1654.         {
  1655.           mPrintf("Overflow!!\n ");
  1656.           free(newString);
  1657.           return;
  1658.  
  1659.         }
  1660.  /*
  1661.   * delete old string:
  1662.   */
  1663.       for (pc = loc,
  1664.            incr = strLen(oldString);
  1665.            *pc = *(pc + incr);  /*
  1666.                                  * Compiler generates a warning for this
  1667.                                  * line
  1668.                                  */
  1669.            pc++) ;
  1670.       textEnd -= incr;
  1671.  /*
  1672.   * make room for new string:
  1673.   */
  1674.       for (pc = textEnd, incr = strLen(newString); pc >= loc; pc--)
  1675.         {
  1676.           *(pc + incr) = *pc;
  1677.  
  1678.         }
  1679.  /*
  1680.   * insert new string:
  1681.   */
  1682.       for (pc = newString; *pc; *loc++ = *pc++) ;
  1683.       if (Global)
  1684.         oldloc = loc = matchString(buf, oldString, oldloc);
  1685.  
  1686.     }
  1687.   while (Global && oldloc != NULL);
  1688.   free(newString);
  1689.  
  1690. }
  1691. /*
  1692.  * * initialArchive() * * This function is responsible for doing an initial
  1693.  * archive of a room.
  1694.  */
  1695. void
  1696. initialArchive(char *fn)
  1697. {
  1698.   char     *TmpMsg,
  1699.            *realfn;
  1700.  
  1701.   TmpMsg = strdup(msgBuf.mbtext);
  1702.   realfn = GetDynamic(strLen(fn) + 15);
  1703.   TranslateFilename(realfn, fn);
  1704.   mPrintf("Doing the initial archive...\n");
  1705.   msgToDisk(realfn, TRUE, 0l, 0l, 0);
  1706.   strCpy(msgBuf.mbtext, TmpMsg);
  1707.   free(TmpMsg);
  1708.   free(realfn);
  1709.  
  1710. }
  1711. /*
  1712.  * * knownHosts() * * This function handles setting systems as hosts.
  1713.  */
  1714. int
  1715. knownHosts(char *name)
  1716. {
  1717.   int       slot,
  1718.             i;
  1719.  
  1720.   if ((slot = CmnNetList(name, &i, ERROR, "does not share this room with you"))
  1721.       == ERROR)
  1722.     return TRUE;
  1723.   if (i == ERROR)
  1724.     if ((i = ListAsShared(name)) == ERROR)
  1725.       return TRUE;
  1726.   CSetMode(netBuf.netRooms[i].mode, ShType);
  1727.   putNet(slot, &netBuf);
  1728.   return TRUE;
  1729.  
  1730. }
  1731. /*
  1732.  * * addToList() * * This function will Add a system to a room networking
  1733.  * list.
  1734.  */
  1735. int
  1736. addToList(char *name)
  1737. {
  1738.   int       slot,
  1739.             i;
  1740.  
  1741.   if ((slot = CmnNetList(name, &i, FALSE, "already networks this room with you"))
  1742.       != ERROR)
  1743.     {
  1744.       if (ListAsShared(name) != ERROR)
  1745.         {
  1746.           putNet(slot, &netBuf);
  1747.  
  1748.         }
  1749.  
  1750.     }
  1751.   return TRUE;
  1752.  
  1753. }
  1754. /*
  1755.  * * ListAsShared() * * This function will find an empty share slot.
  1756.  */
  1757. int
  1758. ListAsShared(char *name)
  1759. {
  1760.   int       i,
  1761.             temp,
  1762.             gen;
  1763.  
  1764.   for (i = 0; i < SHARED_ROOMS; i++)
  1765.     {
  1766.       if ((netBuf.netRooms[i].srgen & 0x8000) != 0)
  1767.         {
  1768.      /*
  1769.       * Salvage attempt
  1770.       */
  1771.           temp = netBuf.netRooms[i].srslot & 0x7fff;
  1772.           gen = netBuf.netRooms[i].srgen & 0x7fff;
  1773.           if (roomTab[temp].rtgen != gen ||     /*
  1774.                                                  * No longer exists!
  1775.                                                  */
  1776.               roomTab[temp].rtflags.SHARED == 0 ||      /*
  1777.                                                          * Not netting
  1778.                                                          */
  1779.               !roomTab[temp].rtflags.INUSE)
  1780.             {
  1781.          /*
  1782.           * no longer exists
  1783.           */
  1784.               break;
  1785.  
  1786.             }
  1787.  
  1788.         }
  1789.       else
  1790.         break;
  1791.  
  1792.     }
  1793.   if (i == SHARED_ROOMS)
  1794.     {
  1795.       mPrintf("Sorry, already sharing %d rooms with %s.\n", SHARED_ROOMS, name);
  1796.       return ERROR;
  1797.  
  1798.     }
  1799.   netBuf.netRooms[i].srslot = thisRoom;
  1800.   netBuf.netRooms[i].lastMess = cfg.newest;
  1801.   netBuf.netRooms[i].srgen = roomBuf.rbgen + (unsigned) 0x8000;
  1802.   CSetMode(netBuf.netRooms[i].mode, PEON);
  1803.   return i;
  1804.  
  1805. }
  1806. /*
  1807.  * * searchForRoom() * * This function checks to see if the current room is
  1808.  * in the current node's * room sharing list.
  1809.  */
  1810. int
  1811. searchForRoom()
  1812. {
  1813.   RoomSearch data;
  1814.  
  1815.   strCpy(data.Room, roomBuf.rbname);
  1816.   EachSharedRoom(thisNet, IsRoomRoutable, NULL, &data);
  1817.   if (data.reason == FOUND)
  1818.     return data.index;
  1819.   return ERROR;
  1820.  
  1821. }
  1822. /*
  1823.  * * getXString()
  1824.  */
  1825. char
  1826. getXString(char *prompt, char *target, int targetSize, char *CR_str,
  1827.            char *dft)
  1828. {
  1829.   return getXInternal(NO_MENU, prompt, target, targetSize, CR_str, dft);
  1830.  
  1831. }
  1832. /*
  1833.  * * getXInternal() * * This is a sophisticated GetString().  Will return
  1834.  * TRUE and FALSE -- read * the code to figure it out.
  1835.  */
  1836. char
  1837. getXInternal(MenuId id, char *prompt, char *target, int targetSize,
  1838.              char *CR_str, char *dft)
  1839. {
  1840.   if (CR_str != NULL && strLen(CR_str) != 0)
  1841.     mPrintf( "C/R = '%s',", CR_str);
  1842.   mPrintf("(ESCape to abort)");
  1843.   exChar = ESC;
  1844.   SysopContinualString(id, prompt, target, targetSize, QUEST_SPECIAL);
  1845.   exChar = '?';
  1846.   if (!onLine())
  1847.     return FALSE;       /*
  1848.                          * Lost carrier
  1849.                          */
  1850.   if (target[0] == ESC)
  1851.     return FALSE;
  1852.   if (CR_str == NULL && target[0] == 0)
  1853.     return FALSE;
  1854.   else if (target[0] == 0)
  1855.     strCpy(target, dft);
  1856.   return TRUE;
  1857.  
  1858. }
  1859. /*
  1860.  * * makeKnown() * * This makes a user knowledgeable about this room.
  1861.  */
  1862. int
  1863. makeKnown(char *user)
  1864. {
  1865.   return doMakeWork(user, roomBuf.rbgen);
  1866.  
  1867. }
  1868. /*
  1869.  * * makeUnknown() * * This makes a user forget about this room.
  1870.  */
  1871. int
  1872. makeUnknown(char *user)
  1873. {
  1874.   return doMakeWork(user, (roomBuf.rbgen + (MAXGEN - 1)) % MAXGEN);
  1875.  
  1876. }
  1877. /*
  1878.  * * doMakeWork() * * This is a worker function.
  1879.  */
  1880. int
  1881. doMakeWork(char *user, int val)
  1882. {
  1883.   int       target;
  1884.  
  1885.   if ((target = findPerson(user, &logTmp)) == ERROR)
  1886.     mPrintf("'%s' not found.\n", user);
  1887.   else
  1888.     {
  1889.       logTmp.lbgen[thisRoom] = (val << GENSHIFT) + MAXVISIT - 1;
  1890.       putLog(&logTmp, target);
  1891.  
  1892.     }
  1893.   return TRUE;
  1894.  
  1895. }
  1896. /*
  1897.  * * killFromList() * * Kills systems from sharing a room.
  1898.  */
  1899. int
  1900. killFromList(char *sysName)
  1901. {
  1902.   int       i,
  1903.             slot;
  1904.  
  1905.   if ((slot = CmnNetList(sysName, &i, TRUE, "does not network this room with you"))
  1906.       == ERROR)
  1907.     return TRUE;
  1908.   netBuf.netRooms[i].srgen = 0;
  1909.   putNet(slot, &netBuf);
  1910.   return TRUE;
  1911.  
  1912. }
  1913. /*
  1914.  * * CmnNetList() * * This does general work on net stuff.
  1915.  */
  1916. int
  1917. CmnNetList(char *name, int *slot, char ShouldBeThere, char *errstr)
  1918. {
  1919.   extern int thisNet;
  1920.  
  1921. /*
  1922.  * This will do required getNet() if it returns true
  1923.  */
  1924.   if (!ReqNodeName("", name, NULL, FALSE, TRUE, FALSE, FALSE, FALSE, &netBuf))
  1925.     return ERROR;
  1926.   *slot = searchForRoom();
  1927.   if ((*slot == ERROR && ShouldBeThere == TRUE) ||
  1928.       (*slot != ERROR && ShouldBeThere == FALSE))
  1929.     {
  1930.       mPrintf("%s %s.", name, errstr);
  1931.       doCR();
  1932.       return ERROR;
  1933.  
  1934.     }
  1935.   return thisNet;
  1936.  
  1937. }
  1938. /*
  1939.  * * WritePrivs() * * Here we give or take away write privs.
  1940.  */
  1941. int
  1942. WritePrivs(char *user)
  1943. {
  1944.   return doMakeWork(user, (DoWritePrivs) ?
  1945.                  (((roomBuf.rbgen + RO_OFFSET)) % MAXGEN) : roomBuf.rbgen);
  1946.  
  1947. }
  1948.